/**
 * This class will be used as the server side of our software.
 * It was designed in order to be independent, and the main will not initiate it.
 * 
 * @author Gil Levinson
 * @author Boris Fradlis
 * @author Yogev Levi
 * @author Hila Porat
 *    
 */


// Imports ************************************************************************************************

package system;
import gui.Server.*;

import java.awt.Component;
import java.awt.Toolkit;
import java.io.IOException;
import java.io.Serializable;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;
import java.util.Timer;
import java.util.TimerTask;
import java.util.Vector;

import javax.swing.JFrame;
import javax.swing.JOptionPane;

import org.joda.time.DateTime;

import gui.Server.ServerGUI;
import ocsf.server.*;
import vcpClasses.Notification;
import vcpClasses.User;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;

// Start of InitiateServer ********************************************************************************

public class InitiateServer extends AbstractServer {

	// Class variables ************************************************************************************
	
	public static JFrame frame;
	public static int guiX = 300, guiY = 200;
	public static ServerGUI serverPanel;
	public static final int DEFAULT_PORT = 5555;
	public static int port = 0;
	private static Date currDateTime;
	public static DataBaseConnection db;
	public static int connectedUsers = 0;
	public static String serverStatus = "";
	public RobotControl robot;
	private static Timer timer = new Timer();
	
	// Constructors **********************************************************************************************
	
	
	/**
	 * Construct an instance of the server and set the DB driver to be ready to connect.
	 * @param port - the port to connect to
	 */
	
	public InitiateServer(int port, String pass) {
			super(port);
			db = new DataBaseConnection(pass);
			robot = new RobotControl(db);
		}
		
	/**
	 * the main method for the server gui
	 * @param args - parameters from the command line
	 */
		
	public static void main(String[] args) {
			 //====== ServerGUI (Login to Database) ======//
				frame = new JFrame();
				frame.addWindowListener(new WindowAdapter() {
					@Override
					public void windowClosing(WindowEvent e) {
						ServerGUI.sv.stopListening();
						ServerGUI.sv.disconnect();
						System.exit(0);
					}
				});
				frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
				frame.setResizable(false);
			
				frame.setTitle("VCP Server v1.0");
				frame.setIconImage(Toolkit.getDefaultToolkit().getImage(VCP.class.getResource("/img/Icon.png")));
				frame.setLocation(guiX, guiY);
				frame.setVisible(true);
				/** add Login Screen to panel frame**/
				serverPanel = new ServerGUI();
				setScreen(null,serverPanel);
				
				TimedStatisticalData();
				
		  } // End of main
	
	public static void TimedStatisticalData(){
		timer.scheduleAtFixedRate(new TimerTask() {
			  @Override
			  public void run() {
				  db.CalculateStatisticalInformation();
				  if (DateTime.now().dayOfWeek().get()==1){
					  db.CalculateWeeklyData();
				  }
			  }
			}, 21600*1000, 86400*1000); // First time after 6 hours, then every 24 hours.
	}
	
	/**
	 * this method will stop the server from listening to clients
	 */
	
	final synchronized public void disconnect()
	{
		try {
			this.close();
			db = null;
		} catch (IOException e) {
			JOptionPane.showMessageDialog(null, "Unable to disconnect !", "Error",
                    JOptionPane.ERROR_MESSAGE);
			e.printStackTrace();
		}
	}
	
	/**
	 * This overriding method handles messages from the client.
	 * The method sorting the queries by type, open the DB connection, and send the queries 
	 * to the proper method under <code> DataBaseConnection </code>.  
	 * @param msg - holds the message from the client
	 * @param client - holds the connection to the client who sent the message
	 * 
	 */

	@Override
	public synchronized void handleMessageFromClient(Object msg, ConnectionToClient client)
	{		
		if (msg instanceof SystemTypes)
		{
			SystemTypes myType = ((SystemTypes)msg);
			Vector<Notification> myVect = null;
			System.out.println("Server received: " + myType.getSysMode() + " from " + client);
			
			// Sort the message by type
			
			switch (myType.getSysMode())
			{
				case LOGIN: // Verify User details
				{
					try
					{
						String IP = client.getInetAddress().toString();
						System.out.println(IP);
						db.openConnectionToDataBase();
						myType = db.verifyUserDetails(myType,IP);
						this.connectedUsers++;
						ServerRunningScreen.updateConnectedUsers(this.connectedUsers);
					}
					catch (Exception e)
					{
						myType.setObj("Cant Handle Request against the DB!!");
						myType.setSysMode(SystemMode.DB_CON_ERR);
					}
					db.closeConnectionToDataBase();
					try 
					{
						
						this.sendToClient(myType,client);
					}
					catch (Exception e) 
					{
						JOptionPane.showMessageDialog(null, "Cant Send Requst to client!!", "Error",
			                    JOptionPane.ERROR_MESSAGE);
						myType.setSysMode(SystemMode.SYS_ERR);
					}
					break;
				}
				case GET_MESSAGE:
					try
					{
						db.openConnectionToDataBase();
						myVect = db.GetMessages(myType);
					}
					catch (Exception e)
					{
						//myType.setObj("Cant Handle Request against the DB!!");
						//myType.setSysMode(SystemMode.DB_CON_ERR);
					}
					db.closeConnectionToDataBase();
					try 
					{
						this.sendToClient(myVect,client);
					}
					catch (Exception e) 
					{
						//myType.setObj("Cant Send Requst to client!!");
						//myType.setSysMode(SystemMode.SYS_ERR);
					}
					break;
				case MARK_READ:
				{
					try
					{
						db.openConnectionToDataBase();
						myType = db.MarkAsRead(myType);
					}
					catch (Exception e)
					{
						myType.setObj("Cant Handle Request against the DB!!");
						myType.setSysMode(SystemMode.DB_CON_ERR);
					}
					db.closeConnectionToDataBase();
					try 
					{
						this.sendToClient(myType,client);
					}
					catch (Exception e) 
					{
						JOptionPane.showMessageDialog(null, "Cant Send Requst to client!!", "Error",
			                    JOptionPane.ERROR_MESSAGE);
						myType.setSysMode(SystemMode.SYS_ERR);
					}
					break;
				}
				case LOGOUT:
				{
					try
					{
						db.openConnectionToDataBase();
						myType = db.LogOut(myType);
						this.connectedUsers--;
						ServerRunningScreen.updateConnectedUsers(this.connectedUsers);
					}
					catch (Exception e)
					{
						JOptionPane.showMessageDialog(null, "Cant Send Requst to client!!", "Error",
			                    JOptionPane.ERROR_MESSAGE);
						myType.setSysMode(SystemMode.DB_CON_ERR);
					}
					db.closeConnectionToDataBase();
					try 
					{
						this.sendToClient(myType,client);
					}
					catch (Exception e) 
					{
						myType.setObj("Cant Send Requst to client!!");
						myType.setSysMode(SystemMode.SYS_ERR);
					}
					break;
				}
				case LOT_INIT:
				{
					try
					{
						db.openConnectionToDataBase();
						myType = db.lotInit(myType);
					}
					catch (Exception e)
					{
						myType.setObj("Cant Handle Request against the DB!!");
						myType.setSysMode(SystemMode.DB_CON_ERR);
					}
					db.closeConnectionToDataBase();
					try 
					{
						this.sendToClient(myType,client);
					}
					catch (Exception e) 
					{
						JOptionPane.showMessageDialog(null, "Cant Send Requst to client!!", "Error",
			                    JOptionPane.ERROR_MESSAGE);
						myType.setSysMode(SystemMode.SYS_ERR);
					}
					break;
				}
				case SET_FULL:
				{
					try
					{
						db.openConnectionToDataBase();
						myType = db.SetFullLot(myType);
					}
					catch (Exception e)
					{
						myType.setObj("Cant Handle Request against the DB!!");
						myType.setSysMode(SystemMode.DB_CON_ERR);
					}
					db.closeConnectionToDataBase();
					try 
					{
						this.sendToClient(myType,client);
					}
					catch (Exception e) 
					{
						JOptionPane.showMessageDialog(null, "Cant Send Requst to client!!", "Error",
			                    JOptionPane.ERROR_MESSAGE);
						myType.setSysMode(SystemMode.SYS_ERR);
					}
					break;
				}
				case GET_ACT_TIME:
				{
					try
					{
						db.openConnectionToDataBase();
						myType = db.getActualTime(myType);
					}
					catch (Exception e)
					{
						myType.setObj("Cant Handle Request against the DB!!");
						myType.setSysMode(SystemMode.DB_CON_ERR);
					}
					db.closeConnectionToDataBase();
					try 
					{
						this.sendToClient(myType,client);
					}
					catch (Exception e) 
					{
						JOptionPane.showMessageDialog(null, "Cant Send Requst to client!!", "Error",
			                    JOptionPane.ERROR_MESSAGE);
						myType.setSysMode(SystemMode.SYS_ERR);
					}
					break;
				}
				case ISSUE_REPORTS:
				{
					try
					{
						db.openConnectionToDataBase();
						System.out.println(1);
						myType = db.getParkingLocationSnapshot(myType);
						System.out.println(2);
					}
					catch (Exception e)
					{
						myType.setObj("Cant Handle Request against the DB!!");
						myType.setSysMode(SystemMode.DB_CON_ERR);
					}
					db.closeConnectionToDataBase();
					try 
					{
						this.sendToClient(myType,client);
					}
					catch (Exception e) 
					{
						JOptionPane.showMessageDialog(null, "Cant Send Requst to client!!", "Error",
			                    JOptionPane.ERROR_MESSAGE);
						myType.setSysMode(SystemMode.SYS_ERR);
					}
					break;
				}
				case READ_ONLY_DATA:
				{
					try
					{
						db.openConnectionToDataBase();
						myType = db.getReadOnlyData(myType);
					
					}
					catch (Exception e)
					{
						myType.setObj("Cant Handle Request against the DB!!");
						myType.setSysMode(SystemMode.DB_CON_ERR);
					}
					db.closeConnectionToDataBase();
					try 
					{
						this.sendToClient(myType,client);
					}
					catch (Exception e) 
					{
						JOptionPane.showMessageDialog(null, "Cant Send Requst to client!!", "Error",
			                    JOptionPane.ERROR_MESSAGE);
						myType.setSysMode(SystemMode.SYS_ERR);
					}
					break;
				}
				
				case UPDATE_RATES:
					try
					{
						db.openConnectionToDataBase();
						myType = db.SetRates(myType);
					}
					catch (Exception e)
					{
						myType.setObj("Cant Handle Request against the DB!!");
						myType.setSysMode(SystemMode.DB_CON_ERR);
					}
					db.closeConnectionToDataBase();
					try 
					{
						this.sendToClient(myType,client);
					}
					catch (Exception e) 
					{
						JOptionPane.showMessageDialog(null, "Cant Send Requst to client!!", "Error",
			                    JOptionPane.ERROR_MESSAGE);
						myType.setSysMode(SystemMode.SYS_ERR);
					}
					break;
				case CHANGE_RATES:
					try
					{
						db.openConnectionToDataBase();
						myType = db.ChangeRates(myType);
					}
					catch (Exception e)
					{
						myType.setObj("Cant Handle Request against the DB!!");
						myType.setSysMode(SystemMode.DB_CON_ERR);
					}
					db.closeConnectionToDataBase();
					try 
					{
						this.sendToClient(myType,client);
					}
					catch (Exception e) 
					{
						JOptionPane.showMessageDialog(null, "Cant Send Requst to client!!", "Error",
			                    JOptionPane.ERROR_MESSAGE);
						myType.setSysMode(SystemMode.SYS_ERR);
					}
					break;
				case NEW_COMPLAINT:
					try
					{
						db.openConnectionToDataBase();
						myType = db.HandleComplaint(myType);
					}
					catch (Exception e)
					{
						myType.setObj("Cant Handle Request against the DB!!");
						myType.setSysMode(SystemMode.DB_CON_ERR);
					}
					db.closeConnectionToDataBase();
					try 
					{
						this.sendToClient(myType,client);
					}
					catch (Exception e) 
					{
						JOptionPane.showMessageDialog(null, "Cant Send Requst to client!!", "Error",
			                    JOptionPane.ERROR_MESSAGE);
						myType.setSysMode(SystemMode.SYS_ERR);
					}
					break;
				case COMPLAINT_REPLY:
					try
					{
						db.openConnectionToDataBase();
						myType = db.HandleComplaintReply(myType);
					}
					catch (Exception e)
					{
						myType.setObj("Cant Handle Request against the DB!!");
						myType.setSysMode(SystemMode.DB_CON_ERR);
					}
					db.closeConnectionToDataBase();
					try 
					{
						this.sendToClient(myType,client);
					}
					catch (Exception e) 
					{
						JOptionPane.showMessageDialog(null, "Cant Send Requst to client!!", "Error",
			                    JOptionPane.ERROR_MESSAGE);
						myType.setSysMode(SystemMode.SYS_ERR);
					}
					break;
				case REMINDER_REPLY:
				{
					try
					{
						db.openConnectionToDataBase();
						myType = db.HandleReminderReply(myType);
					}
					catch (Exception e)
					{
						myType.setObj("Cant Handle Request against the DB!!");
						myType.setSysMode(SystemMode.DB_CON_ERR);
					}
					db.closeConnectionToDataBase();
					try 
					{
						this.sendToClient(myType,client);
					}
					catch (Exception e) 
					{
						JOptionPane.showMessageDialog(null, "Cant Send Requst to client!!", "Error",
			                    JOptionPane.ERROR_MESSAGE);
						myType.setSysMode(SystemMode.SYS_ERR);
					}
					break;
				}
				case GET_RATES:
					try
					{
						db.openConnectionToDataBase();
						myType = db.getRatesForLot(myType);
					}
					catch (Exception e)
					{
						myType.setObj("Cant Handle Request against the DB!!");
						myType.setSysMode(SystemMode.DB_CON_ERR);
					}
					db.closeConnectionToDataBase();
					try 
					{
						this.sendToClient(myType,client);
					}
					catch (Exception e) 
					{
						JOptionPane.showMessageDialog(null, "Cant Send Requst to client!!", "Error",
			                    JOptionPane.ERROR_MESSAGE);
						myType.setSysMode(SystemMode.SYS_ERR);
					}
					break;
				case NEW_BALANCE:
					try
					{
						db.openConnectionToDataBase();
						myType = db.GetNewBalance(myType);
					}
					catch (Exception e)
					{
						myType.setObj("Cant Handle Request against the DB!!");
						myType.setSysMode(SystemMode.DB_CON_ERR);
					}
					db.closeConnectionToDataBase();
					try 
					{
						this.sendToClient(myType,client);
					}
					catch (Exception e) 
					{
						JOptionPane.showMessageDialog(null, "Cant Send Requst to client!!", "Error",
			                    JOptionPane.ERROR_MESSAGE);
						myType.setSysMode(SystemMode.SYS_ERR);
					}
					break;
				case NEW_ORDER:
					try
					{
						db.openConnectionToDataBase();
						myType = db.creatNewOrder(myType);
					}
					catch (Exception e)
					{
						e.printStackTrace();
						myType.setObj("Cant Handle Request against the DB!!");
						myType.setSysMode(SystemMode.DB_CON_ERR);
					}
					db.closeConnectionToDataBase();
					try 
					{
						this.sendToClient(myType,client);
					}
					catch (Exception e) 
					{
						JOptionPane.showMessageDialog(null, "Cant Send Requst to client!!", "Error",
			                    JOptionPane.ERROR_MESSAGE);
						myType.setSysMode(SystemMode.SYS_ERR);
					}
					break;
				case CLOSE_ORDER:
					try
					{
						db.openConnectionToDataBase();
						myType = db.closeOrder(myType);
					}
					catch (Exception e)
					{
						myType.setObj("Cant Handle Request against the DB!!");
						myType.setSysMode(SystemMode.DB_CON_ERR);
					}
					db.closeConnectionToDataBase();
					try 
					{
						this.sendToClient(myType,client);
					}
					catch (Exception e) 
					{
						JOptionPane.showMessageDialog(null, "Cant Send Requst to client!!", "Error",
			                    JOptionPane.ERROR_MESSAGE);
						myType.setSysMode(SystemMode.SYS_ERR);
					}
					break;
				case CANCEL_ORDER:
					try
					{
						db.openConnectionToDataBase();
						myType = db.cancelOrder(myType);
					}
					catch (Exception e)
					{
						myType.setObj("Cant Handle Request against the DB!!");
						myType.setSysMode(SystemMode.DB_CON_ERR);
					}
					db.closeConnectionToDataBase();
					try 
					{
						this.sendToClient(myType,client);
					}
					catch (Exception e) 
					{
						JOptionPane.showMessageDialog(null, "Cant Send Requst to client!!", "Error",
			                    JOptionPane.ERROR_MESSAGE);
						myType.setSysMode(SystemMode.SYS_ERR);
					}
					break;
				case NEW_SUBSCRIPTION:
					try
					{
						db.openConnectionToDataBase();
						myType = db.createNewSubscription(myType);
					}
					catch (Exception e)
					{
						myType.setObj("Cant Handle Request against the DB!!");
						myType.setSysMode(SystemMode.DB_CON_ERR);
					}
					db.closeConnectionToDataBase();
					try 
					{
						this.sendToClient(myType,client);
					}
					catch (Exception e) 
					{
						JOptionPane.showMessageDialog(null, "Cant Send Requst to client!!", "Error",
			                    JOptionPane.ERROR_MESSAGE);
						myType.setSysMode(SystemMode.SYS_ERR);
					}
					break;
				case RENEW_SUBSCRIPTION:
					try
					{
						db.openConnectionToDataBase();
						myType = db.renewSubscription(myType);
					}
					catch (Exception e)
					{
						myType.setObj("Cant Handle Request against the DB!!");
						myType.setSysMode(SystemMode.DB_CON_ERR);
					}
					db.closeConnectionToDataBase();
					try 
					{
						this.sendToClient(myType,client);
					}
					catch (Exception e) 
					{
						JOptionPane.showMessageDialog(null, "Cant Send Requst to client!!", "Error",
			                    JOptionPane.ERROR_MESSAGE);
						myType.setSysMode(SystemMode.SYS_ERR);
					}
					break;
				case NEW_REPORT:
					try
					{
						db.openConnectionToDataBase();
						switch (myType.getObj().toString())
						{
						case "Performance Report":
							myType = db.GeneratePerformanceReport(myType);
							break;
						case "Exception Report":
							myType = db.GenerateOperationExceptionReport(myType);
							break;
						case "Quarter Report":
							myType = db.GenerateOperationExceptionReport(myType);
							break;
					}
					/* ADD MORE REPORTS HERE */	
					}
					catch (Exception e)
					{
						myType.setObj("Cant Handle Request against the DB!!");
						myType.setSysMode(SystemMode.DB_CON_ERR);
					}
					db.closeConnectionToDataBase();
					try 
					{
						this.sendToClient(myType,client);
					}
					catch (Exception e) 
					{
						JOptionPane.showMessageDialog(null, "Cant Send Requst to client!!", "Error",
			                    JOptionPane.ERROR_MESSAGE);
						myType.setSysMode(SystemMode.SYS_ERR);
					}
					break;
				case TRACK_ORDER:
					try
					{
						db.openConnectionToDataBase();
						myType = db.trackOrder(myType);
					}
					catch (Exception e)
					{
						myType.setObj("Cant Handle Request against the DB!!");
						myType.setSysMode(SystemMode.DB_CON_ERR);
					}
					db.closeConnectionToDataBase();
					try 
					{
						this.sendToClient(myType,client);
					}
					catch (Exception e) 
					{
						JOptionPane.showMessageDialog(null, "Cant Send Requst to client!!", "Error",
			                    JOptionPane.ERROR_MESSAGE);
						myType.setSysMode(SystemMode.SYS_ERR);
					}
					break;
				case ENTER_CAR:
					try
					{
						db.openConnectionToDataBase();
						myType = robot.InsertVehicle(myType);
					}
					catch (Exception e)
					{
						e.printStackTrace();
						myType.setObj("Cant Handle Request against the DB!!");
						myType.setSysMode(SystemMode.DB_CON_ERR);
					}
					db.closeConnectionToDataBase();
					try 
					{
						this.sendToClient(myType,client);
					}
					catch (Exception e) 
					{
						JOptionPane.showMessageDialog(null, "Cant Send Requst to client!!", "Error",
			                    JOptionPane.ERROR_MESSAGE);
						myType.setSysMode(SystemMode.SYS_ERR);
					}
					break;
				case REMOVE_CAR:
					try
					{
						db.openConnectionToDataBase();
						myType = robot.RemoveVehicle(myType);
					}
					catch (Exception e)
					{
						myType.setObj("Cant Handle Request against the DB!!");
						myType.setSysMode(SystemMode.DB_CON_ERR);
					}
					db.closeConnectionToDataBase();
					try 
					{
						this.sendToClient(myType,client);
					}
					catch (Exception e) 
					{
						JOptionPane.showMessageDialog(null, "Cant Send Requst to client!!", "Error",
			                    JOptionPane.ERROR_MESSAGE);
						myType.setSysMode(SystemMode.SYS_ERR);
					}
					break;
				default:
				{
					System.out.println(1);
					String str = "";
					switch (myType.getSysMode())
					{
						case SET_DAMAGED:
							str = "DAMAGED";
							break;
						case SET_MAINTENANCE:
							str = "MAINTENANCE";
							break;
						case SET_OCCUPIED:
							str = "OCCUPIED";
							break;
						case SET_FREE:
							str = "FREE";
							break;
						case SET_SAVED:
							str = "SAVED";
					}
					try
					{
						System.out.println(2);
						db.openConnectionToDataBase();
						myType = db.setSpecialStatus(str, myType);
						System.out.println(3);
					}
					catch (Exception e)
					{
						myType.setObj("Cant Handle Request against the DB!!");
						myType.setSysMode(SystemMode.DB_CON_ERR);
					}
					db.closeConnectionToDataBase();
					try 
					{
						System.out.println(4);
						this.sendToClient(myType,client);
					}
					catch (Exception e) 
					{
						JOptionPane.showMessageDialog(null, "Cant Send Requst to client!!", "Error",
			                    JOptionPane.ERROR_MESSAGE);
						myType.setSysMode(SystemMode.SYS_ERR);
					}
					break;
				}
			}
		}		
	} // End handleMessageFromClient
	
	/**
	 * this method will send object to the client who approached the server
	 * @param obj - the object to send
	 * @param client - the client to send to
	 */
	
	public void sendToClient(Serializable obj, ConnectionToClient client){
		try {
			client.sendToClient(obj);
		} catch (IOException e) {
			e.printStackTrace();
		}	
	}

	/**
	 * this method will control the screen switching for the server's GUI
	 * @param oldComp - the screen to remove
	 * @param newComp - the screen to set
	 */

	public static void setScreen(Component oldComp, Component newComp)
	{		
		if (oldComp != null)
		{
			oldComp.setVisible(false);
			frame.remove(oldComp);
		}
		if (newComp != null)
		{
			frame.getContentPane().add(newComp);
			frame.setLocation(guiX,guiY);
			frame.setSize(newComp.getSize());
			newComp.setVisible(true);
		}
	}
	
	/**
	 * this method the the current date and time in specific format
	 * @return the string  the current date and time 
	 */
	
	public static String getDateTime(){
		DateFormat dateFormat = new SimpleDateFormat("EEE, MMM d, yyyy",Locale.US);
		currDateTime = new Date();
		String dateTime = new String(dateFormat.format(currDateTime));
		return dateTime;
	}


	
}//End of InitiateServer

	


